package cli.command;

import cli.view.Formats;
import cli.view.LoadingIcon;
import cli.view.ProgressBar;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class Copy implements Command {
    private Scanner in;
    private PrintWriter out;
    private Path currentPath;
    private boolean r = false; // 递归复制
    private Path logPath = null; // 输出重定向

    public Copy(Scanner in, PrintWriter out, Path currentPath) {
        this.in = in;
        this.out = out;
        this.currentPath = currentPath;
    }

    @Override
    public void handle(String[] args) {
        int pos = 0;
        if (pos >= args.length) {
            out.println("请输入源地址和目的地址!");
            return;
        }
        if (args[pos].startsWith("-")) {
            setOptions(args[pos++]);
        }
        if (pos >= args.length) {
            out.println("请输入源地址和目的地址!");
            return;
        }
        Path src = currentPath.resolve(args[pos++]);
        if (pos >= args.length) {
            out.println("请输入目的地址！");
            return;
        }
        Path dst = currentPath.resolve(args[pos++]);
        if (pos < args.length) {
            if (args[pos].contains(">")) {
                pos++;
                if (pos < args.length) {
                    logPath = currentPath.resolve(args[pos++]);
                }
            }
        }
        if (logPath != null) {
            new Thread(()->{
                try {
                    out = new PrintWriter(Files.newBufferedWriter(logPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING), true);
                    if (r) {
                        copyRecur(src, dst);
                    } else {
                        copy(src, dst);
                    }
                    out.println("复制完成。");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        } else {
            if (r) {
                copyRecur(src, dst);
            } else {
                copy(src, dst);
            }
        }
    }

    private void setOptions(String options) {
        r = options.contains("r");
    }

    private void copy(Path src, Path dst) {
        try {
            if (Files.isDirectory(src) && Files.list(src).count() > 0) {
                out.println("文件夹非空！");
                return;
            }
            LoadingIcon.wait(out, () -> {
                try {
                    Files.copy(src, dst);
                } catch (IOException e) {
                    e.printStackTrace(out);
                }
            });
        } catch (FileAlreadyExistsException e) {
            out.println("同名文件已存在！");
        } catch (IOException e) {
            out.println("复制失败！");
        }
    }

    private void copyRecur(Path src, Path dst) {
        int[] _filesNum = {0};
        long[] _totalSize = {0};
        LoadingIcon.wait(out, () -> {
            try {
                Files.walk(src)
                        .filter(Files::isRegularFile)
                        .forEach(p -> {
                            try {
                                _filesNum[0]++;
                                _totalSize[0] += Files.size(p);
                            } catch (IOException ignored) {
                            }
                        });
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        out.printf("文件数目：%s\t总大小：%s\n", Formats.number(_filesNum[0]), Formats.memorySize(_totalSize[0]));
        if (logPath == null && _totalSize[0] > 1_000_000) {
            out.println("是否复制？");
            String s = in.nextLine();
            if (!(s.contains("是") || s.contains("y") || s.contains("Y")))
                return;
        }
        ProgressBar progressBar = new ProgressBar(_filesNum[0], _totalSize[0]);
        long[] _copiedSize = {0};
        int[] _copiedNum = {0};
        progressBar.wait(out, () -> {
            try {
                Files.walkFileTree(src, new FileVisitor<Path>() {
                    @Override
                    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                        try {
                            Files.createDirectory(dst.resolve(src.relativize(dir)));
                            return FileVisitResult.CONTINUE;
                        } catch (FileAlreadyExistsException e) {
                            out.println("同名文件已存在！");
                            return FileVisitResult.TERMINATE;
                        } catch (IOException e) {
                            out.println("复制失败！");
                        }
                        return FileVisitResult.TERMINATE;
                    }

                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                        try {
                            Files.copy(file, dst.resolve(src.relativize(file)));
                            _copiedNum[0]++;
                            _copiedSize[0] += attrs.size();
                            progressBar.update(_copiedNum[0], _copiedSize[0]);
                            return FileVisitResult.CONTINUE;
                        } catch (IOException e) {
                            out.println("复制文件"+currentPath.relativize(file)+"失败！");
                        }
                        return FileVisitResult.TERMINATE;
                    }

                    @Override
                    public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
                        out.println("无法访问文件"+currentPath.relativize(file)+"!");
                        return FileVisitResult.TERMINATE;
                    }

                    @Override
                    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                        return FileVisitResult.CONTINUE;
                    }
                });
            } catch (FileNotFoundException e) {
                out.println("文件不存在！");
            } catch (IOException e) {
                out.println("复制失败！");
            }
        });
    }
}
